﻿"""This is a basic database manipulator"""

class base:
	"This class is a base connector"
	#print (self.__doc__)
	#beginning of class suite
	def __init__(self):
		self.driver=None
		self.user=None
		self.passwd=None
		self.host=None
		self.port=None
		self.charset=None
		self.base=None
		self.sid=None
		self.dsn=None
		self.cstring=None
		self.state=False
		self.log=None
		self.conn=None
		self.cursor=None
		self.variable=None

	def open(self):
		"This function opens connection to base"
		#print (self.open.__doc__)
		return

	def connect(self):
		"This function is a wrapper for open function"
		#print (self.connect.__doc__)
		#beginning of function suite
		if self.state==False:
			self.open()
			self.state=True
			self.log='Connected'
		else:
			self.log='Already connected'
		#end of function suite
		return 

	def close(self):
		"This function closes connection to base"
		#print (self.close.__doc__)
		if self.cursor: self.cursor.close()
		if self.conn: self.conn.close()
		return

	def disconnect(self):
		"This function is a wrapper for close function"
		#print (self.disconnect.__doc__)
		#beginning of function suite
		if self.state==True:
			self.close()
			self.conn=None
			self.cursor=None
			self.state=False
			self.log='Disconnected'
		else:
			self.log='Already disconnected'
		#end of function suite
		return

	def execute_in_cycle(self, string, row=None, lim=0):
		"This function executes a statement optional for row of values"
		#beginning of function suite
		#end of function suite
		return

	def execute_many_in_cycle(self, string, rows):
		"This function executes a statement for rows of values"
		#beginning of function suite
		#end of function suite
		return

	def fetch_in_cycle(self, string, row=None, lim=0):
		"This function fetches a statement optional for row of values"
		#beginning of function suite
		#end of function suite
		return self.execute_in_cycle(string, row, lim)

	def commit_in_cycle(self, string=None):
		"This function executes a statement and commits it"
		#beginning of function suite
		#end of function suite
		return

	def execute(self, string, row=None, lim=0):
		"This function connects to base and executes a statement optional for row of values"
		#beginning of function suite
		#end of function suite
		return

	def execute_many(self, string, rows):
		"This function connects to base and executes a statement for rows of values"
		#beginning of function suite
		#end of function suite
		return

	def fetch(self, string, row=None, lim=0):
		"This function connects to base and fetches a statement optional for row of values"
		#beginning of function suite
		#end of function suite
		return self.execute(string, row, lim)

	def commit(self, string=None):
		"This function connects to base, executes a statement and commits it"
		#beginning of function suite
		#end of function suite
		return

	#end of class base suite

class relations:
	"This class counts and select records for relations"
	#beginning of class suite
	#print (relations.__doc__)
	#Инициализация:
	def __init__(self):
		self.log=None
		self.base=base()

	def type(self,type):
		'Function returns base specific type sign'
		return

	def catalogue(self, type):
		'Function returns sql string for the catalogue'
		return

	def field(self, type):
		'Function returns sql string for the field'
		return

	def list(self, names):
		'Function returns sql string for the list of names'
		return "('"+"','".join(names)+"')"
		
	def condition(self, type, word):
		'Function returns base specific condition for table/view catalogue'
		return ''

	def CountString(self, type, names):
		'Function returns sql string for count'
		#Если аргумент names не равен ['']
		if names!=['']:
			string='SELECT COUNT(*) FROM'+' '+self.catalogue(type)+' '+'WHERE'+' '+self.field(type)+' IN '+self.list(names)+self.condition(type,'and')
		else:
			string='SELECT COUNT(*) FROM'+' '+self.catalogue(type)+self.condition(type,'where')
		return string

	def CountlikeString(self, type, names):
		'Function returns sql string for countlike'
		result='SELECT COUNT(*) FROM'+' '+self.catalogue(type)+' '+'WHERE'+' '+'('
		for name in names:
			name="'"+'%'+name+'%'+"'"
			result+=self.field(type)+' '+'LIKE'+' '+name+' '+'OR'+' '
		return result[:-4]+')'+self.condition(type,'and')

	def SelectString(self, type, names):
		'Function returns sql string for select'
		#Если аргумент names не равен ['']
		if names!=['']:
			string='SELECT * FROM'+' '+self.catalogue(type)+' '+'WHERE'+' '+self.field(type)+' IN '+self.list(names)+self.condition(type,'and')
		else:
			string='SELECT * FROM'+' '+self.catalogue(type)+self.condition(type,'where')
		return string

	def SelectlikeString(self, type, names):
		'Function returns sql string for selectlike'
		result='SELECT * FROM'+' '+self.catalogue(type)+' '+'WHERE'+' '+'('
		for name in names:
			name="'"+'%'+name+'%'+"'"
			result+=self.field(type)+' '+'LIKE'+' '+name+' '+'OR'+' '
		return result[:-4]+')'+self.condition(type,'and')

	#Определяется базовая функция подсчета количества таблиц и видов
	def count(self, like, type, names):
		'''Function counts relations'''
		#beginning of function suite
		#print(self.count.__doc__)
		#Переменной result присваивается результат подсчета
		if like:
			result=self.base.fetch(self.CountlikeString(type, names))[0][0]
		else:
			result=self.base.fetch(self.CountString(type, names))[0][0]
		#В переменную класса log записывается лог
		self.log='counted'
		#end of function suite
		#Функция возвращает число таблиц и видов с заданными именами
		return result

	#Определяется базовая функция выборки записей для таблиц и видов
	def select(self, like, type, names):
		'''Function selects relations'''
		#beginning of function suite
		#print(self.select.__doc__)
		#Переменной result присваивается результат выборки
		if like:
			result=self.base.fetch(self.SelectlikeString(type, names))[0][0]
		else:
			result=self.base.fetch(self.SelectString(type, names))[0][0]
		#В переменную класса log записывается лог
		self.log='selected'
		#end of function suite
		#Функция возвращает записи для таблиц и видов с заданными именами
		return result

	#Определяется функция подсчета количества таблиц
	def CountTables(self, names=['']):
		'''Function counts tables'''
		#beginning of function suite
		#print(self.CountTables.__doc__)
		#end of function suite
		#Функция возвращает число таблиц с заданными именами
		return self.count(False, self.type('t'), names)

	#Определяется функция подсчета количества видов
	def CountViews(self, names=['']):
		'''Function counts views'''
		#beginning of function suite
		#print(self.CountViews.__doc__)
		#end of function suite
		#Функция возвращает число видов с заданными именами
		return self.count(False, self.type('v'), names)

	#Определяется функция подсчета количества похожих таблиц
	def CountlikeTables(self, names=['']):
		'''Function counts tables'''
		#beginning of function suite
		#print(self.CountlikeTables.__doc__)
		#end of function suite
		#Функция возвращает число таблиц с похожим именами
		return self.count(True, self.type('t'), names)

	#Определяется функция подсчета количества похожих видов
	def CountlikeViews(self, names=['']):
		'''Function counts views'''
		#beginning of function suite
		#print(self.CountlikeViews.__doc__)
		#end of function suite
		#Функция возвращает число видов с похожим именами
		return self.count(True, self.type('v'), names)

	#Определяется функция выборки таблиц
	def SelectTables(self, names=['']):
		'''Function selects tables'''
		#beginning of function suite
		#print(self.SelectTables.__doc__)
		#end of function suite
		#Функция возвращает выборку таблиц с заданными именами
		return self.select(False, self.type('t'), names)

	#Определяется функция выборки видов
	def SelectViews(self, names=['']):
		'''Function selects views'''
		#beginning of function suite
		#print(self.SelectViews.__doc__)
		#end of function suite
		#Функция возвращает выборку видов с заданными именами
		return self.select(False, self.type('v'), names)

	#Определяется функция выборки похожих таблиц
	def SelectlikeTables(self, names=['']):
		'''Function selects like tables'''
		#beginning of function suite
		#print(self.SelectlikeTables.__doc__)
		#end of function suite
		#Функция возвращает выборку таблиц с похожими именами
		return self.select(True, self.type('t'), names)

	#Определяется функция выборки похожих видов
	def SelectlikeViews(self, names=['']):
		'''Function selects like views'''
		#beginning of function suite
		#print(self.SelectlikeViews.__doc__)
		#end of function suite
		#Функция возвращает выборку видов с похожими именами
		return self.select(True, self.type('v'), names)

	#end of class relations suite

class view:
	"This class is a manipulater for views"
	#beginning of class suite
	#print (self.__doc__)
	#Инициализация методов класса
	def __init__(self):
		self.base=base()
		self.relations=relations()
		self.relations.base=self.base
		self.log=None
		self.param=''
		self.case=True

	def SqlCase(self, string):
		"Function converts name to upper or lower case depends on self.case value"
		if self.case:
			string=string.upper()
		else:
			string=string.lower()
		return string

	def SqlFields(self, fields):
		"Function returns sql string for the view fields"
		return self.SqlCase('('+', '.join(fields)+')')

	def SqlCreate(self, name, fields, query, param):
		"Function returns sql string for the view create query"
		return "CREATE "+param+" VIEW "+self.SqlCase(name)+" "+self.SqlFields(fields)+" AS "+query

	def SqlDrop(self, name):
		"Function returns sql string for the view drop query"
		return "DROP VIEW "+self.SqlCase(name)

	def SqlColumns(self, columns=[]):
		'Function returns sql string for the list of columns'
		if columns==[]:
			result='*'
		else:
			result=', '.join(columns)
		return self.SqlCase(result)

	def SqlClause(self, clause=[]):
		'Function returns sql string for the list of clause lists'
		#beginning of function suite
		if clause==[]:
			result=''
		else:
			result=' '+'WHERE'+' '
			for row in clause:
				for item in row:
					result+=str(item)+' '
		#end of function suite
		return self.SqlCase(result)

	def countself(self, name):
		'''Function counts views with the name of the view'''
		#beginning of function suite
		#print(self.count.__doc__)
		#end of function suite
		#Функция возвращает результат выполнения функции self.FdbArticlesToImport.CountViews() с именем вида в качестве аргумента
		return self.relations.CountViews([self.SqlCase(name)])

	def create(self, name, fields, query, param):
		'''Function creates the view '''
		#beginning of function suite
		#print(FdbArticlesToImport.create.__doc__)
		#Подсчетывается количество видов с данным именем
		before=self.countself(name)
		#Если количество=0
		if before==0:
			# Выполняется запрос на создание вида в Firebird:
			self.base.commit(self.SqlCreate(name, fields, query, param))
			#Подсчетывается количество видов с данным именем
			result=self.countself(name)-before
			#В переменную класса log записывается лог
			self.log='created'
		else:
			#Переменной result присваивается нулевое значение
			result=0
			#В переменную класса log записывается лог
			self.log='exists'
		#end of function suite
		#Функция возвращает количество созданных видов
		return result

	def drop(self, name):
		'''Function drops the view'''
		#beginning of function suite
		#print(FdbArticlesToImport.drop.__doc__)
		#Подсчетывается количество видов с данным именем
		before=self.countself(name)
		#Если количество не равно нулю:
		if before!=0:
			# Выполняется запрос на удаление вида в Firebird:
			self.base.commit(self.SqlDrop(name))
			#Подсчетывается количество видов с данным именем
			result=before-self.countself(name)
			self.log='droped'
		else:
			#Переменной result присваивается нулевое значение
			result=0
			#В переменную класса log записывается лог
			self.log='does not exist'
		#end of function suite
		#Функция возвращает количество уничтоженных видов
		return result

	def select(self, name, columns=[], clause=[], lim=0):
		'Function selects columns from the view where clause is satisfied'
		#print (self.select.__doc__)
		#beginning of function suite
		#Переменной result присваивается результат запроса
		result=self.base.fetch('SELECT'+' '+self.SqlColumns(columns)+' '+'FROM'+' '+self.SqlCase(name)+self.SqlClause(clause), None, lim)
		#end of function suite
		return result
	
	def generate(self, name, columns=[], clause=[], lim=1000):
		'Function selects columns from the view where clause is satisfied'
		#print (self.select.__doc__)
		#beginning of function suite
		#Переменной result присваивается результат запроса
		result=self.base.generate('SELECT'+' '+self.SqlColumns(columns)+' '+'FROM'+' '+self.SqlCase(name)+self.SqlClause(clause), None, lim)
		#end of function suite
		return result

	def iterate(self, name, columns=[], clause=[], start=None, stop=None, step=None):
		'Function selects columns from the view where clause is satisfied'
		#print (self.select.__doc__)
		#beginning of function suite
		#Переменной result присваивается результат запроса
		result=self.base.generate('SELECT'+' '+self.SqlColumns(columns)+' '+'FROM'+' '+self.SqlCase(name)+self.SqlClause(clause), None, start, stop, step)
		#end of function suite
		return result

	def count(self, name, clause=[]):
		'Function selects columns from the view where clause is satisfied'
		#print (self.select.__doc__)
		#beginning of function suite
		#Переменной result присваивается результат запроса
		result=self.base.fetch('SELECT COUNT(*) '+'FROM'+' '+self.SqlCase(name)+self.SqlClause(clause))[0][0]
		#end of function suite
		return result

	#end of class view suite

class table(view):
	"This class is a manipulater for tables"
	#beginning of class suite
	#print (self.__doc__)
	#Инициализация методов класса
	def __init__(self):
		super(table, self).__init__()
		self.param=''
		self.seq=True
		self.seq_postfix='_'
		self.constraint_postfix='_'
		self.newline='\n'
		#self.newline='''
		#'''

	def seq_create_extra(self, name, primary):
		return ''
	
	def tab_create_extra(self, name, comment):
		return ''
	
	def tab_drop_extra(self):
		return ''
	
	def seq_drop_extra(self):
		return ''

	def SqlCreateSeq(self, name, primary):
		"Function returns sql string for the sequence create query"
		return 'CREATE SEQUENCE'+' '+self.SqlCase(name+self.seq_postfix)+self.seq_create_extra(name, primary)
	
	def SqlCreateTab(self, param, name, fields, primary, comment):
		"Function returns sql string for the view create query"
		result='CREATE'+' '+param+' '+'TABLE'+' '+self.SqlCase(name)+' '+self.newline
		result+='('+self.newline
		for row in fields:
			result+=self.SqlCase(row[0])+' '+self.SqlCase(row[1])+' '+self.SqlCase(row[2])+','+self.newline
		if primary!='':
			result+='CONSTRAINT'+' '+self.SqlCase(name)+self.constraint_postfix+' '+'PRIMARY KEY ('+self.SqlCase(primary)+')'
		else:
			result=result[:-2]
		result+=self.newline+')'
		result+=self.tab_create_extra(name, comment)
		return result

	def SqlDropSeq(self, name):
		"Function returns sql string for the sequence drop query"
		return 'DROP SEQUENCE'+' '+self.SqlCase(name+self.seq_postfix)+' '+self.seq_drop_extra()
	
	def SqlDropTab(self, name):
		"Function returns sql string for the table drop query"
		return 'DROP TABLE'+' '+self.SqlCase(name)+' '+self.tab_drop_extra()

	def SqlVariables(self, list):
		"Function returns base specific list of variables"
		return '('+((self.base.variable+', ')*len(list))[:-2]+')'

	def SqlColumnValue(self, dict):
		#Function returns sql string for {column:value} dictionary
		#beginning of function suite
		#print(self.SqlColumnValue.__doc__)
		result=[]
		for item in dict:
			result+=[item+'='+"'"+str(dict[item])+"'"]
		result=', '.join(result)
		#end of function suite
		#Функция возвращает sql строку
		return result
	
#	def SqlValues(self, values):
#		"Function returns sql string for the view fields"
#		result=[]
#		for item in values:
#			result+=[str(item)]
#		result="('"+"', '".join(result)+"')"
#		return result

#	def SqlVariables(self, fields):
#		"Function returns base specific list of variables"
#		return '('+((self.relations.variable+', ')*len(fields))[:-2]+')'
	
	def countself(self, name):
		'''Function counts tables with the name of the table'''
		#beginning of function suite
		#print(self.count.__doc__)
		#end of function suite
		#Функция возвращает результат выполнения функции self.FdbArticlesToImport.CountViews() с именем вида в качестве аргумента
		return self.relations.CountTables([self.SqlCase(name)])
		
	def create(self, param, name, fields, primary, comment):
		'''Function creates the table'''
		#beginning of function suite
		#print(self.create.__doc__)
		#Подсчетывается количество таблиц с данным именем
		before=self.countself(name)
		#Если количество=0
		if before==0:
			#Если признак необходимости создания последовательности истина, то:
			if self.seq:
				# Выполняется запрос на создание последовательности:
				self.base.commit(self.SqlCreateSeq(name, primary))
			# Выполняется запрос на создание таблицы:
			self.base.commit(self.SqlCreateTab(param, name, fields, primary, comment))
			#Подсчетывается количество таблиц с данным именем
			result=self.countself(name)-before
			#В переменную класса log записывается лог
			self.log='created'
		else:
			#Переменной result присваивается нулевое значение
			result=0
			#В переменную класса log записывается лог
			self.log='exists'
		#end of function suite
		#Функция возвращает количество созданных видов
		return result

	def drop(self, name):
		'''Function drops the table'''
		#beginning of function suite
		#print(self.drop.__doc__)
		#Подсчетывается количество видов с данным именем
		before=self.countself(name)
		#Если количество не равно нулю:
		if before!=0:
			#Если признак необходимости создания последовательности истина, то:
			if self.seq:
				# Выполняется запрос на уничтожение последовательности:
				self.base.commit(self.SqlDropSeq(name))
			# Выполняется запрос на уничтожение таблицы:
			self.base.commit(self.SqlDropTab(name))
			#Подсчетывается количество видов с данным именем
			result=before-self.countself(name)
			self.log='droped'
		else:
			#Переменной result присваивается нулевое значение
			result=0
			#В переменную класса log записывается лог
			self.log='does not exist'
		#end of function suite
		#Функция возвращает количество уничтоженных видов
		return result

	def delete(self, name, clause=[]):
		'Function deletes records where clause is satisfied'
		#print (self.delete.__doc__)
		#beginning of function suite
		#Переменной result присваивается результат запроса
		before=self.count(name, clause)
		self.base.execute('DELETE'+' '+'FROM'+' '+self.SqlCase(name)+self.SqlClause(clause))
		result=before-self.count(name, clause)
		#end of function suite
		return result

	def update(self, name, dict, clause=[]):
		'Function updates columns where clause is satisfied'
		#print (self.update.__doc__)
		#beginning of function suite
		#Переменной result присваивается результат запроса
		result=self.count(name, clause)
		self.base.execute('UPDATE'+' '+self.SqlCase(name)+' '+'SET'+' '+self.SqlColumnValue(dict)+' '+self.SqlClause(clause))
		#end of function suite
		return result

	def insert(self, name, columns, values):
		'Function inserts records'
		#print (self.update.__doc__)
		#beginning of function suite
		before=self.count(name)
		self.base.execute_many(('INSERT INTO'+' '+self.SqlCase(name)+' '+'('+self.SqlColumns(columns)+')'+' '+'VALUES'+' '+self.SqlVariables(columns)),values)
		
		result=self.count(name)-before
		#end of function suite
		return result

	#end of class table suite

class aview:
	"This class is a manipulator for a view"
	#beginning of class suite
	#print (self.__doc__)
	def __init__(self):
		self.view=view()
		self.name=''
		self.fields=[]
		self.query=''
		self.param=self.view.param

	#Заменяется родительская функция
	def SqlCase(self):
		"Function returns sql string for the view name"
		return self.view.SqlCase(self.name)

	#Заменяется родительская функция
	def SqlFields(self):
		"Function returns sql string for the view fields"
		return self.view.SqlFields(self.fields)

	#Заменяется родительская функция
	def SqlCreate(self):
		"Function returns sql string for the view create query"
		return self.view.SqlCreate(self.name, self.fields, self.query, self.param)

	#Заменяется родительская функция
	def SqlDrop(self):
		"Function returns sql string for the view drop query"
		return self.view.SqlDrop(self.name)

	#Определяется функция подсчета количества видов с данным именем
	def countself(self):
		return self.view.countself(self.name)

	#Определяется функция создания вида
	def create(self):
		return self.view.create(self.name, self.fields, self.query, self.param)

	#Определяется функция уничтожения вида
	def drop(self):
		return self.view.drop(self.name)

	#Определяется функция выборки из вида
	def select(self, columns=[], clause=[], lim=0):
		'Function selects columns from the view where clause is satisfied'
		#print (self.select.__doc__)
		#beginning of function suite
		#end of function suite
		return self.view.select(self.name, columns, clause, lim)
	
	#Определяется функция выборки из вида
	def generate(self, columns=[], clause=[], lim=1000):
		'Function selects columns from the view where clause is satisfied'
		#print (self.select.__doc__)
		#beginning of function suite
		#end of function suite
		return self.view.generate(self.name, columns, clause, lim)

	#Определяется функция выборки из вида
	def iterate(self, columns=[], clause=[], start=None, stop=None, step=None):
		'Function selects columns from the view where clause is satisfied'
		#print (self.select.__doc__)
		#beginning of function suite
		#end of function suite
		return self.view.iterate(self.name, columns, clause, start, stop, step)

	#Определяется функция подсчета строк в виде
	def count(self, clause=[]):
		'Function selects columns from the view where clause is satisfied'
		#print (self.select.__doc__)
		#beginning of function suite
		#end of function suite
		return self.view.count(self.name, clause)

	#end of class aview suite

class atable:
	"This class is a manipulator for a table"
	#beginning of class suite
	#print (self.__doc__)
	def __init__(self):
		self.table=table()
		self.param=self.table.param
		self.name=''
		self.fields=[[]]
		self.primary=''
		self.comment=''

	def SqlFields(self):
		return self.table.SqlFields(self.fields)

	def seq_create_extra(self):
		return self.table.seq_create_extra(self.name, self.primary)
		
	def tab_create_extra(self):
		return self.table.tab_create_extra(self.name, self.comment)
		
	def tab_drop_extra(self):
		return self.table.tab_drop_extra()
	
	def seq_drop_extra(self):
		return self.table.seq_drop_extra()
		
	def SqlCreateSeq(self):
		return self.table.SqlCreateSeq(self.name, self.primary)
	
	def SqlCreateTab(self):
		return self.table.SqlCreateTab(self.param, self.name, self.fields, self.primary, self.comment)
	
	def SqlDropSeq(self):
		return self.table.SqlDropSeq(self.name)
	
	def SqlDropTab(self):
		return self.table.SqlDropTab(self.name)

	def countself(self):
		return self.table.countself(self.name)

	def create(self):
		return self.table.create(self.param, self.name, self.fields, self.primary, self.comment)

	def drop(self):
		return self.table.drop(self.name)

	def select(self, columns=[], clause=[], lim=0):
		return self.table.select(self.name, columns, clause, lim)
		
	def generate(self, columns=[], clause=[], lim=1000):
		return self.table.generate(self.name, columns, clause, lim)

	def iterate(self, columns=[], clause=[], start=None, stop=None, step=None):
		return self.table.iterate(self.name, columns, clause, start, stop, step)

	def count(self, clause=[]):
		return self.table.count(self.name, clause)

	def delete(self, clause=[]):
		return self.table.delete(self.name, clause)

	def update(self, dict, clause=[]):
		return self.table.update(self.name, dict, clause)

	def insert(self, columns, values):
		return self.table.insert(self.name, columns, values)

	#end of class atable suite